<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module";
import {GUI} from "three/examples/jsm/libs/lil-gui.module.min.js";
import {onMounted, ref} from "vue";
import {addLargeGroundPlane, initCamera, initDefaultLighting, initWebGLRenderer} from "@/book/ThreeUtils";

const canvas = ref<any>();

const scene = new THREE.Scene();

const renderer = initWebGLRenderer(undefined);

const camera = initCamera(undefined);
camera.position.set(-30, 40, 30);

initDefaultLighting(scene, undefined);

const stat = Stats();

// const orbitControl = new OrbitControls(camera, renderer.domElement);
// orbitControl.update();

const groundPlane = addLargeGroundPlane(scene, true);
groundPlane.position.y = 0;

const cubeGeom = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(cubeGeom, cubeMaterial);
cube.name = "正方体";
cube.castShadow = true;
cube.position.set(-10, 4, 0);
scene.add(cube);

const sphereGeom = new THREE.SphereGeometry(4, 20, 20);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0x7777ff });
const sphere = new THREE.Mesh(sphereGeom, sphereMaterial);
sphere.name = "球体";
sphere.castShadow = true;
sphere.position.set(20, 0, 2);
scene.add(sphere);

const cylinderGeom = new THREE.CylinderGeometry(2, 2, 20);
const cylinderMaterial = new THREE.MeshStandardMaterial({color: 0x77ff77});
const cylinder = new THREE.Mesh(cylinderGeom, cylinderMaterial);
cylinder.name = "圆柱体";
cylinder.castShadow = true;
cylinder.position.set(0, 0, 1);
scene.add(cylinder);

const ambientLight = new THREE.AmbientLight(0x353535);
scene.add(ambientLight);

const controlObj = {
  rotationSpeed: 0.02,
  bouncingSpeed: 0.03,
  scalingSpeed: 0.03,
  showRay: false,
};

let step = 0;
let scalingStep = 0;
let rayLine = null;

const gui = new GUI();
// @ts-ignore
gui.add(controlObj, 'rotationSpeed', 0, 0.5).step(0.1).name("正方体旋转速度");
// @ts-ignore
gui.add(controlObj, 'bouncingSpeed', 0, 0.5).step(0.1).name("球体弹跳速度");
// @ts-ignore
gui.add(controlObj, 'scalingSpeed', 0, 0.5).step(0.1).name("圆柱体变形速度");
// @ts-ignore
gui.add(controlObj, 'showRay').name("鼠标指示射线").onChange((e) => {
  if (rayLine) {
    scene.remove(rayLine);
  }
});

document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);

const render = () => {
  stat.update();

  cube.rotation.x += controlObj.rotationSpeed;
  cube.rotation.y += controlObj.rotationSpeed;
  cube.rotation.z += controlObj.rotationSpeed;

  // bounce the sphere up and down
  step += controlObj.bouncingSpeed;
  sphere.position.x = 20 + ( 10 * (Math.cos(step)));
  sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));

  // scale the cylinder
  scalingStep += controlObj.scalingSpeed;
  const scaleX = Math.abs(Math.sin(scalingStep / 4));
  const scaleY = Math.abs(Math.cos(scalingStep / 5));
  const scaleZ = Math.abs(Math.sin(scalingStep / 7));
  cylinder.scale.set(scaleX, scaleY, scaleZ);

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});

function onDocumentMouseDown(event) {
  let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
  vector = vector.unproject(camera);

  const rayCaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
  const intersects = rayCaster.intersectObjects([sphere, cylinder, cube]);

  if (intersects.length > 0) {
    console.log(' onDocumentMouseDown intersect: ', intersects);
    // @ts-ignore
    // intersects[0].object.material.transparent = intersects[0].object.material.transparent === false;
    // @ts-ignore
    // intersects[0].object.material.opacity = intersects[0].object.material.opacity === 0.1 ? 1 : 0.1;

    // @ts-ignore
    intersects[0].object.material = new THREE.MeshStandardMaterial({
      color: intersects[0].object.material.color,
      transparent: intersects[0].object.material.transparent === false,
      opacity: intersects[0].object.material.opacity === 0.3 ? 1 : 0.3,
    });
  }
}

function onDocumentMouseMove(event) {
  if (controlObj.showRay) {
    let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
    vector = vector.unproject(camera);

    const rayCaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    const intersects = rayCaster.intersectObjects([sphere, cylinder, cube]);

    if (intersects.length > 0) {

      const points = [];
      points.push(new THREE.Vector3(-30, 39.8, 30));
      points.push(intersects[0].point);

      const rayLineMat = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: 0.6});
      const rayLineGeom = new THREE.TubeGeometry(new THREE.CatmullRomCurve3(points), 60, 0.001);

      if (rayLine) {
        scene.remove(rayLine);
      }

      if (controlObj.showRay) {
        rayLine = new THREE.Mesh(rayLineGeom, rayLineMat);
        scene.add(rayLine);
      }
    }
  }
}

</script>
